//
// Creted by Administrator on 2023/3/17.
#include "http_server.h"
#include "sys/socket.h"
#include <cassert>
#include "log/logger_main.h"
#include "net/e_poller.h"
#include "http/http_connection.h"

void http_server::start() {

    int ret = ::socket(AF_INET, SOCK_STREAM, 0);//返回监听文件标识符
    assert(ret >= 0);//保证大于等于0
    //设置非阻塞
    int getFlag = fcntl(ret, F_GETFL);
    fcntl(ret, F_SETFL, getFlag | O_NONBLOCK);

    socket_lis = std::make_unique<class socket>(ret);
    socket_lis->set_reused(true);//端口重用
    socket_lis->bind(this->port);
    socket_lis->listen();
    //添加到树 todo
    e_poller::get_instance().add_fd_ori(socket_lis->get_fd(), EPOLLIN | EPOLLET);
    LOG_INFO("server 初始化成功.....");
    loop();
}

http_server::http_server(int16_t port) : port(port) {

    dum_fd = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
    LOG_INFO("空占用描述符{}", dum_fd);
    //初始化线程池
    threadPool.setThreadNum(24);
    //threadPool.set_thread_num(24);
    threadPool.start();

}

void http_server::loop() {

    while (true)
    {

        {
            auto res = this->t.check_timer();
            for (const auto &item : res) {
                holder.closed_by_fd(item);
                LOG_INFO("--check timer{}", item);
            }
        }

        int size = e_poller::get_instance().epoll_wait(1);

        for (int i = 0; i < size; ++i) {
            auto t = e_poller::get_instance()[i];

            event_handler(t);
        }


    }


}

void http_server::do_acceptor() {

    //如果是ET模式这边必须循环到标识符小于0 且错误不等于eagainn;

    //获取一个新的连接
    while (true) {


        class socket sock = this->socket_lis->accept(this->socket_lis->get_fd());
        //说明新连接接受失败
        if (sock.get_fd() < 0) {
            if (errno == EAGAIN) {
                return;
                break;//无链接
            }
            //说明连接符用完了
            if (errno == EMFILE) {
                close(dum_fd);//关闭空文件标识
                dum_fd = ::accept(socket_lis->get_fd(), nullptr, nullptr);
                ::close(dum_fd);
                dum_fd = open("/dev/null", O_CLOEXEC | O_RDONLY);
                LOG_INFO("文件标识符已经占满");
            } else {
                LOG_ERROR("接受新连接发生未知错误{}", strerror(errno));
            }
            break;
        }
        LOG_INFO("接受了一个新的连接 fd==>{}", sock.get_fd());
        //建立连接


        t.add_timer(sock.get_fd(), addTime(Timestamp::now(), 5).microSecondsSinceEpoch());
        std::make_shared<http_connection>(holder, std::move(sock))->start();
    }

}

void http_server::event_handler(epoll_event &t) {

    if (t.data.fd == this->socket_lis->get_fd()) {
        do_acceptor();
    } else if (((t.events & EPOLLHUP) && !(t.events & EPOLLIN)) || (t.events & EPOLLERR)) {
        //此时出错应该关闭连接的_________
        //EPOLLRDHUP 对面关闭 英语中计算机hup 有挂断的意思
        //EPOLLHUP  表示读写都关闭
        // EPOLLERR 自己出错
        holder.closed_by_fd(t.data.fd);//关闭连接
        LOG_INFO("一个连接被关闭文件标识符{}", std::to_string(t.data.fd));

    } else if (t.events & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) {
        //获取连接
        //   this->t.extern_time(t.data.fd);
        auto con = this->holder.get_connection_by_fd(t.data.fd);
        //链接的读事件
        threadPool.addTask([=, this] {
            LOG_INFO("read  fd==>{}", std::to_string(t.data.fd));
            con->do_read();

//            {
//                this->t.extern_time(t.data.fd);
//            }
        });

        //读入事件
    } else if (t.events & EPOLLOUT) {
        auto  con=this->holder.get_connection_by_fd(t.data.fd);
        //链接的读事件
        threadPool.addTask([=]() {
            con->do_write();

            {
                this->t.extern_time(t.data.fd);
            }

        });
        LOG_INFO("write fd==>{}",std::to_string(t.data.fd));
    }else {
        holder.closed_by_fd(t.data.fd);//关闭连接
        LOG_INFO("未知错误{}", std::to_string(t.data.fd));
    }

}


